package com.xiyang.web.configuration.security;

import com.xiyang.web.configuration.security.handler.LoginFailureHandler;
import com.xiyang.web.configuration.security.handler.LoginSuccessHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

/**
 * SecurityContextPersistenceFilter
 * {@link org.springframework.security.web.context.SecurityContextPersistenceFilter}
 * 该类在所有的filter之前，是从{@link org.springframework.security.web.context.SecurityContextRepository}取出用户认证信息，
 * 默认实现类@{@link org.springframework.security.web.context.HttpSessionSecurityContextRepository},
 * 其会从Session中取出已认证用户的信息，提高效率，避免每一次请求都要查询用户认证信息。
 * 取出之后会放入{@link org.springframework.security.core.context.SecurityContextHolder}中，以便其他filter使用，
 * SecurityContextHolder使用ThreadLocal存储用户认证信息，保证了线程之间的信息隔离，最后再finally中清楚该信息
 * ⇩
 *
 * @author xiyang.ycj
 * @since Jun 25, 2019 01:52:43 AM
 */
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {


    private final LoginSuccessHandler successHandler;
    private final LoginFailureHandler failureHandler;
    private final CustomUserDetailsServiceImpl userDetailsService;
    private final UnauthorizedEntryPoint unauthorizedEntryPoint;
    private final DisableUrlSessionFilter disableUrlSessionFilter;

    @Autowired
    public WebSecurityConfig(LoginSuccessHandler successHandler, LoginFailureHandler failureHandler, CustomUserDetailsServiceImpl userDetailsService, UnauthorizedEntryPoint unauthorizedEntryPoint, DisableUrlSessionFilter disableUrlSessionFilter) {
        this.successHandler = successHandler;
        this.failureHandler = failureHandler;
        this.userDetailsService = userDetailsService;
        this.unauthorizedEntryPoint = unauthorizedEntryPoint;
        this.disableUrlSessionFilter = disableUrlSessionFilter;
    }

    ///@Autowired
    //private CustomFilterSecurityInterceptor filterSecurityInterceptor;


    /**
     * spring5.0之后，spring security必须设置加密方法否则会报
     * There is no PasswordEncoder mapped for the id "null"
     *
     * @return 加密
     */
    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder(4);
    }

    /**
     * 主要配置身份认证来源，也就是用户及其角色。
     *
     * @param auth
     * @throws Exception
     */
    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }


    /**
     * Override this method to configure {@link WebSecurity}. For example, if you wish to
     * ignore certain requests.
     *
     * @param web
     */
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/jquery/**", "/layui/**", "/login/**", "/date_xiyang.js", "/step/**");
    }

    /**
     * 要配置路径，也就是资源的访问权限（是否需要认证，需要什么角色等）。
     *
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http
                .authorizeRequests()
                .antMatchers("/login_page", "/logins").permitAll()
                ///.antMatchers("/index").hasRole("pdd")
                .anyRequest().authenticated()
                .and()
                //.httpBasic();
                .formLogin()
                .loginPage("/login_page")
                // 设置登录逻辑url 注意：上面忽略了login，所以这里用这个
                .loginProcessingUrl("/login_processing")
                .successHandler(successHandler)
                .failureHandler(failureHandler)
                .and()
                .logout()
                .logoutSuccessUrl("/logins")
                // 用户在退出后Http session 失效 默认为true
                .invalidateHttpSession(true);

        http.csrf().disable();
        /* 防止再次登录 此时不会踢出之前的登录者，而是先登录者建立了唯一session，在他注销或关闭浏览器之前，不允许异地再次登录。*/
        ///  http.sessionManagement().maximumSessions(1).maxSessionsPreventsLogin(true);
        // 防止再次登录 此时会踢出之前的登录者，再去发出请求会跳转到过期url。
        http.sessionManagement()
                // session 超时跳向的url
                .maximumSessions(1)
                .expiredUrl("/logins");
        http.headers().frameOptions().disable(); /* 关闭spring security 禁用frame的设置 */
        // 用来解决匿名用户访问无权限资源时的异常 区分是否是ajax请求
        http.exceptionHandling().authenticationEntryPoint(unauthorizedEntryPoint);
        http.addFilterBefore(disableUrlSessionFilter, UsernamePasswordAuthenticationFilter.class);
        // http.addFilterAt(filterSecurityInterceptor, FilterSecurityInterceptor.class);
    }
}
